﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Threading;
using System.Globalization;
 
namespace MiReaderLib
{
    class Program
    {
        static SerialPort sp = null;

        // 0      1     2      3       lencmd-2 lencmd-1
        // STX:1  ID:1  LEN:1  DATA:N  BCC:1    ETX:1

        enum ReaderVersion
        { 
            UNKNOWN      = 0, 
            MULTI_ISO_1_1   ,
            TA_1_00_63      ,
        }

        enum SelectResponse
        {
            //
        }

        static string dameVersion()
        {
            byte[] cmd = Cmd.build ((byte)'v');
            sp.WriteBytes(cmd);
            byte[] res = readRespCmd();
            Cmd.isValidCmd(cmd);
            byte[] data= Cmd.getCmdData(res);
            return Encoding.ASCII.GetString(
                data, 0, data.Length
            );
        }

        static string select()
        {
            return string.Empty;    
        }

        static byte[] readRespCmd()
        {
            int stx = sp.ReadByte();
            int sid = sp.ReadByte();
            int len = sp.ReadByte();
            byte[] cmd = new byte[len + 5];
            cmd[0] = (byte)stx;
            cmd[1] = (byte)sid;
            cmd[2] = (byte)len;
            for (int i = 0; i < len; i++)
                cmd[3 + i] = (byte)sp.ReadByte();
            cmd[cmd.Length - 2] = (byte)sp.ReadByte();
            cmd[cmd.Length - 1] = (byte)sp.ReadByte();
            if (!Cmd.isValidCmd(cmd))
                throw new Exception("comando no válido");
            return cmd;
        }

        static void Main(string[] args)
        {
            sp = new SerialPort("COM1", 115200);
            sp.ReadTimeout = 250;
            sp.Open();

            string version = dameVersion();

            if ("MultiISO 1.1" == version)
            {
                sp.WriteBytes(Cmd.build((byte)'.'));
                sp.WriteBytes(Cmd.build((byte)'.'));
                trabajar("02 01 04 77 70 0A 01 09 03");
                trabajar("02 01 04 77 70 0C 04 0A 03");
                trabajar("02 01 04 77 70 0F 0A 07 03");
                trabajar("02 01 04 77 70 10 00 12 03");
                trabajar("02 01 04 77 70 11 03 10 03");
                trabajar("02 01 04 77 70 12 27 37 03");
                trabajar("02 01 04 77 70 14 0A 1C 03");
                trabajar("02 01 04 77 70 15 25 32 03");
                trabajar("02 01 04 77 70 16 00 14 03");
                trabajar("02 01 04 77 70 17 10 05 03");
                trabajar("02 01 04 77 70 18 10 0A 03");
                trabajar("02 01 04 77 70 19 10 0B 03");
                trabajar("02 01 04 77 70 1A 20 38 03");
                trabajar("02 01 04 77 70 1D 05 1A 03");
                trabajar("02 01 04 77 70 1E EB F7 03");
                trabajar("02 01 04 77 70 1C 00 1E 03");
                trabajar("02 01 04 77 70 1F 00 1D 03");
                trabajar("02 01 04 77 70 0D 20 2F 03");
                trabajar("02 01 04 77 70 0B 52 5B 03");
                trabajar("02 01 04 77 70 13 00 11 03");
                trabajar("02 01 04 77 70 1B 00 19 03");
                trabajar("02 01 04 77 70 20 00 22 03");
                trabajar("02 01 04 77 70 0E 81 8D 03");
            }

            for (; ; )
            {
                // select
                for (;;)
                {
                    byte[] cmd = Cmd.build((byte)'s');
                    sp.WriteBytes(cmd);
                    byte[] res = readRespCmd();
                    Console.Write(Environment.TickCount);
                    Console.Write(" :: ");
                    showCmd(res);
                    if ((res != null) &&
                        (res[3] != 'N') &&
                        (res[3] != '?') &&
                        (res[3] != 'X'))
                        break;
                }

                // loguin
                {
                    byte[] cmd = Cmd.build(
                        (byte)'l', // comando en si...
                        0x07, // sector 
                        0xAA, // tipo clave
                        0xC5, 0xC4, 0x73, 0xC2, 0xC1, 0xC0 // clave
                    );
                    sp.WriteBytes(cmd);
                    byte[] res = readRespCmd();
                    //byte[] res = trabajar(cmd);

                    if (res[3] != 'L')
                    {
                        Console.WriteLine("No se pudo loguear, respuesta {0}", (char)res[3]);
                        int foo = 0;
                        continue;
                    }
                }

                // pos = bloque + (sector * 4)
                //  28 = 0      + (7      * 4)
                // 0 1 2 3      0
                // 4 5 6 7      1
                // 8 9 10 11    2
                // 12 13 14 15  3
                // 16 17 18 19  4
                // 20 21 22 23  5
                // 24 25 26 27  6
                // 28 29 30 31  7
                // leo pos 28 (sector 7, bloque 0)
                
                // read sector 7, bloque 0 (pos 28)
                {
                    //byte[] cmd = Cmd.build((byte)'r', (byte)'b', 28);
                    //showCmd(cmd);
                    //sp.WriteBytes(cmd);
                    //byte [] res = readRespCmd();
                    //showCmd(res);
                }

                // lee múltiples bloques de un saque...
                { 
                    // leo 3 bloques, desde el sector 28
                    // r d 28 3 = 0x72 0x64 0x1C 0x03
                    byte[] cmd = Cmd.build((byte)'r', (byte)'d', 28, 3);
                    sp.WriteBytes(cmd);
                    byte[] resp = readRespCmd();
                    showCmd(resp);
                }
            }
            
            Console.WriteLine("Presiona una tecla para continuar");
            Console.ReadLine();
        }

        static byte[] trabajar(byte[] cmd)
        {
            showCmd(cmd);
            sp.WriteBytes(cmd);
            try
            {
                byte[] respuesta = readRespCmd();
                showCmd(respuesta);
                Console.WriteLine("----------------------------------");
                return respuesta;
            }
            catch (Exception exx)
            {
                Console.WriteLine(exx.Message);
                return null;
            }
        }

        static byte [] trabajar(string comando)
        {
            return trabajar(hexaStringToBuff(comando));
        }

        static void showCmd(byte[] buff)
        {
            if (Cmd.isValidCmd(buff))           
                Console.Write("CHKSUM OK  ");
            else
                Console.Write("CHKSUM BAD ");

            Console.Write("[ ");
            foreach (byte @byte in buff)
                Console.Write("{0:X2} ", @byte);

            Console.Write("] ");

            // creo un aux buffer sin cabecera STX,ID,LEN y sin BCC,ETX
            // solo con la parte DATA
            byte[] auxBuff = new byte[buff.Length - 5];
            Array.Copy(buff, 3, auxBuff, 0, buff.Length - 5);

            Console.ForegroundColor = ConsoleColor.Cyan;
            foreach (byte @byte in auxBuff) {
                Console.Write(traducirByte(@byte));
                Console.Write(" ");
            }
            Console.ForegroundColor = ConsoleColor.Gray;
            Console.WriteLine();
        }

        static byte[] hexaStringToBuff (string hexaString)
        {
            List<byte> lst = new List<byte>();

            foreach (string part in hexaString.Split(' ', ',', ';', ':'))
                lst.Add( byte.Parse(part, NumberStyles.HexNumber));

            return lst.ToArray();
        }

        static string traducirByte(byte b)
        {
            switch (b)
            {
                case 0: return "NUL";
                case 1: return "SOH";
                case 2: return "STX";
                case 3: return "ETX";
                case 4: return "EOT";
                case 5: return "ENQ";
                case 6: return "ACK";
                case 7: return "BEL";
                case 8: return "BS ";
                case 9: return "TAB";
                case 10: return "LF ";
                case 11: return "VT ";
                case 12: return "FF ";
                case 13: return "CR ";
                case 14: return "SO ";
                case 15: return "SI ";
                case 16: return "DLE";
                case 17: return "DC1";
                case 18: return "DC2";
                case 19: return "DC3";
                case 20: return "DC4";
                case 21: return "NAK";
                case 22: return "SYN";
                case 23: return "ETB";
                case 24: return "CAN";
                case 25: return "EM ";
                case 26: return "SUB";
                case 27: return "ESC";
                case 28: return "FS ";
                case 29: return "GS ";
                case 30: return "RS ";
                case 31: return "US ";
                case 32: return "SPA";
                case 127: return "DEL";
                default:
                    return new string((char)b, 1);
            }
        }
    }
}
